<!DOCTYPE html>
<html lang="en">

<head>
	<meta charset="UTF-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>Document</title>
</head>

<body>
	<script>
		// 获取时间
		const getTime = (min, max) => {
			return min + Math.floor(Math.random() * (max - min))
		}

		// 获取数据
		const getData = function (i) {
			return new Promise(resolve => {
				const time = getTime(500, 1000);
				setTimeout(() => {
					console.log('current task index:', i, ' time:', time)
					resolve(i)
				}, time)
			})
		}
		
		// 任务队列函数
		function TaskQueue() {
			this.tasks = [];
			// 运行状态
			this.runningState = '';

			// 执行完毕后回调
			this.finishCallback = null;
		}

		// 添加任务
		TaskQueue.prototype.addTask = function (task) {
			this.tasks.push(task);
		};

		// 执行任务
		TaskQueue.prototype.runTasks = function (concurrency) {
			if (this.runningState === 'pending') {
				return console.log('TaskQueue running!');
			}
			this.runningState = 'pending'; // 状态切换为运行中
			let taskRunIndex = 0;
			const self = this;
			const { tasks, finishCallback } = this;
			const runningTasks = []; // 正在运行的任务

			// 运行任务方法
			function _walkTask() {
				// 是否为最后一个任务
				if (taskRunIndex >= tasks.length) {
					return Promise.resolve();
				}

				// 当前任务函数
				const task = tasks[taskRunIndex];
				
				// 执行当前任务
				const taskPromise = task(taskRunIndex).finally(() => {
					const _taskRunIndex = runningTasks.indexOf(taskPromise);
					runningTasks.splice(_taskRunIndex, 1); // 在队列中清空

					// 任务结束回调
					if (taskRunIndex === tasks.length && runningTasks.length === 0) {
						this.runningState = '';
						this.finishCallback && this.finishCallback(); // 任务结束回调
					}
				});

				// 添加到正在执行的任务队列中
				runningTasks.push(taskPromise);
				
				let res = null;

				// 如果当前队列中执行的数量，等于最大的限制数量
				if (runningTasks.length >= concurrency) {
					res = Promise.race(runningTasks);
				} else {
					res = Promise.resolve();
				}

				// 任务索引累加
				taskRunIndex++;

				// 继续执行
				return res.then((d) => _walkTask.call(self));
			};
			return _walkTask.call(self);
		};

		// 执行结束方法
		TaskQueue.prototype.finish = function (callback) {
			this.finishCallback = callback;
		}

		// 创建一个任务队列
		const requestTask = new TaskQueue(); 

		// 添加任务
		for (let i = 0; i < 20; i++) {
			requestTask.addTask(getData)
		};

		// 绑定结束事件
		requestTask.finish(() => {
			console.log('Task结束啦~~~');
		});

		// 开始执行任务, 并发为3个
		requestTask.runTasks(2);
	</script>
</body>

</html>